package com.the_last.filter;

import com.auth0.jwt.interfaces.Claim;
import com.the_last.constant.RedisKeyConstant;
import com.the_last.mapper.UserMapper;
import com.the_last.pojo.bo.LoginUser;
import com.the_last.pojo.po.User;
import com.the_last.utils.JwtUtil;
import com.the_last.utils.RedisUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;

@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Resource
    private RedisUtil redisUtil;
    
    @Resource
    private UserDetailsService userDetailsService;
    
    @Resource
    private ExecutorService executorService;
    
    @Resource
    private UserMapper userMapper;
    
    /**
     * 获取用户权限版本号
     * 优先从Redis缓存获取，如果缓存中不存在则从数据库查询并更新缓存
     *
     * @param userId 用户ID
     * @return 权限版本号，如果用户不存在则返回null
     */
    private Integer getUserPermVersion(Long userId) {
        String cacheKey = RedisKeyConstant.USER_PERM_VERSION_KEY + userId;
        
        // 1. 尝试从Redis缓存获取
        Integer permVersion = redisUtil.getObject(cacheKey, Integer.class);
        if (permVersion != null) {
            log.debug("从Redis缓存获取用户权限版本: userId={}, permVersion={}", userId, permVersion);
            return permVersion;
        }
        
        // 2. 缓存中不存在，从数据库查询
        try {
            User user = userMapper.selectById(userId);
            if (user != null) {
                permVersion = user.getPermVersion();
                // 3. 异步更新Redis缓存，设置30分钟过期时间
                if (permVersion != null) {
                    Integer finalPermVersion = permVersion;
                    executorService.execute(() -> {
                        redisUtil.set(cacheKey, finalPermVersion, 72 * RedisUtil.ONE_HOUR);
                        log.debug("异步更新Redis缓存: userId={}, permVersion={}", userId, finalPermVersion);
                    });
                }
                log.debug("从数据库获取用户权限版本: userId={}, permVersion={}", userId, permVersion);
                return permVersion;
            } else {
                log.warn("数据库中未找到用户: userId={}", userId);
                return null;
            }
        } catch (Exception e) {
            log.error("查询用户权限版本失败: userId={}", userId, e);
            return null;
        }
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        
        String token = request.getHeader("Authorization");
        
        if (!StringUtils.hasText(token)) {
            filterChain.doFilter(request, response);
            return;
        }
        
        if (token.startsWith("Bearer ")) {
            token = token.substring(7);
        }
        
        try {
            boolean isTokenValid = JwtUtil.verify(token);
            if (!isTokenValid) {
                log.warn("Invalid JWT token: {}", token.substring(0, Math.min(token.length(), 20)) + "...");
                filterChain.doFilter(request, response);
                return;
            }
            
            Map<String, Claim> claims = JwtUtil.getClaims(token);
            Claim userIdClaim = claims.get("userId");
            
            if (userIdClaim == null) {
                log.warn("JWT token missing userId claim");
                filterChain.doFilter(request, response);
                return;
            }
            
            Long userId = userIdClaim.asLong();
            
            LoginUser loginUser = redisUtil.getObject(RedisKeyConstant.LOGIN_KEY + userId, LoginUser.class);
            
            // 使用封装的方法获取权限版本（优先从Redis缓存，没有则从数据库查询）
            Integer permVersion = getUserPermVersion(userId);
            
            if (Objects.isNull(loginUser)) {
                log.debug("User not found in Redis cache for userId: {}", userId);
                filterChain.doFilter(request, response);
                return;
            }
            
            if (loginUser.getUser() == null || !loginUser.getUser().getStatus().equals(1)) {
                log.warn("User account disabled for userId: {}", userId);
                filterChain.doFilter(request, response);
                return;
            }
            
            
            if (!loginUser.getUser().getPermVersion().equals(permVersion)) {
                // 权限版本不匹配，重新获取权限（处理permVersion为null的情况）
                log.debug("权限版本不匹配，重新加载用户权限: userId={}, loginUser.permVersion={}, redis/db.permVersion={}",
                        userId, loginUser.getUser().getPermVersion(), permVersion);
                loginUser = (LoginUser) userDetailsService.loadUserByUsername(loginUser.getUsername());
                LoginUser finalLoginUser = loginUser;
                executorService.execute(() -> redisUtil.set(RedisKeyConstant.LOGIN_KEY + userId, finalLoginUser, 30 * 60));
            }
            
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            
        } catch (Exception e) {
            log.error("JWT authentication failed", e);
        }
        
        filterChain.doFilter(request, response);
    }
}
